package com.jason.campusinn.view;

import java.text.DecimalFormat;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.ImageView;
import android.widget.Scroller;

public class VerticalRuler extends ImageView {
    private Matrix matrix; // Matrix used to perform rotations
    private int wheelHeight, wheelWidth; // height and width of the view
    private int top; // the current top of the wheel (calculated in
    // wheel divs)
    private float totalDistance; // variable that counts the total rotation
    // during a given rotation of the wheel by the
    // user (from ACTION_DOWN to ACTION_UP)
    private int divCount = 30; // no of divisions in the wheel
    private float divDistance; // angle of each division
    private float selectedPosition; // the section currently selected by the
    // user.
    private boolean snapToCenterFlag = true; // variable that determines whether
    // to snap the
    // wheel to the center of a div or not
    private Context context;
    private ChangeListener wheelChangeListener;
    private Paint mPaint;// 画笔
    private TextPaint curvePaint;
    private float pointCurve = 165f;

    int longLineB_H = 3;
    int shortLineS_H = 2;
    int longLineB_W = 40;
    int shortLineS_W = 20;

    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;
    private double startY;
    private double maxDistance;
    private double minDistance;
    private int mMinVelocity;

    public VerticalRuler(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    // initializations
    private void init(Context context) {
        this.context = context;
        mScroller = new Scroller(getContext());
        this.setScaleType(ScaleType.MATRIX);
        selectedPosition = 0;
        mMinVelocity = ViewConfiguration.get(getContext())
                .getScaledMinimumFlingVelocity();
        // initialize the matrix only once
        if (matrix == null) {
            matrix = new Matrix();
        } else {
            matrix.reset();
        }
        mPaint = new Paint();
        curvePaint = new TextPaint();
        this.setOnTouchListener(new WheelTouchListener());
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            Log.d("computeScroll",
                    "computeScroll init to reBound" + mScroller.getFinalY());
            if (mScroller.getCurrY() == mScroller.getFinalY()) {
                // Log.d("computeScroll",
                // "computeScroll to reBound" + mScroller.getFinalY());
                reBound();
            } else {
                // Log.d("computeScroll", "computeScroll reBound startY" +
                // startY);
                int yPosition = mScroller.getCurrY();
                if ((int) startY == yPosition) {
                    // moveCurve((float) (mScroller.getFinalY() - startY));
                    // startY = yPosition;
                    reBound();
                } else {
                    // Log.d("computeScroll", "computeScroll reBound getCurrY"
                    // + yPosition);
                    moveCurve((float) (yPosition - startY));
                    startY = yPosition;
                }
            }
        }
    }

    /**
     * Add a new listener to observe user selection changes.
     *
     * @param wheelChangeListener
     */
    public void setChangeListener(ChangeListener wheelChangeListener) {
        this.wheelChangeListener = wheelChangeListener;
    }

    /**
     * Returns the position currently selected by the user.
     *
     * @return the currently selected position between 1 and divCount.
     */
    public float getSelectedPosition() {
        return selectedPosition;
    }

    /**
     * Set no of divisions in the wheel menu.
     *
     * @param divCount no of divisions.
     */
    public void setDivCount(int divCount) {
        this.divCount = divCount;

    }

    /**
     * Set the snap to center flag. If true, wheel will always snap to center of
     * current section.
     *
     * @param snapToCenterFlag
     */
    public void setSnapToCenterFlag(boolean snapToCenterFlag) {
        this.snapToCenterFlag = snapToCenterFlag;
    }

    /**
     * Set a different top position. Default top position is 0. Should be set
     * after {#setDivCount(int) setDivCount} method and the value should be
     * greater than 0 and lesser than divCount, otherwise the provided value
     * will be ignored.
     *
     * @param newTopDiv
     */
    public void setAlternateTopDiv(int newTopDiv) {

        if (newTopDiv < 0 || newTopDiv >= divCount)
            return;
        else
            top = newTopDiv;

        selectedPosition = top;
    }

    /*
     * We need this to get the dimensions of the view. Once we get those, We can
     * scale the image to make sure it's proper, Initialize the matrix and align
     * it with the views center.
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        // method called multiple times but initialized just once
        if (wheelHeight == 0 || wheelWidth == 0) {
            wheelHeight = h;
            wheelWidth = w;
//            Log.d("test", "wheelHeight:" + wheelHeight);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);
        divDistance = (float) (wheelHeight / 30);
        minDistance = (30 - 16 * 5) * divDistance;
        maxDistance = 13 * 5 * divDistance;
//        Log.d("test", "totalDistance:" + totalDistance);
//        Log.d("test", "divDistance:" + divDistance);
        for (int i = -16; i <= 14; i++) {
            drawLine(canvas, i * 5 * divDistance);
            drawCurve(canvas, i * divDistance * 5, (float) (170 - i * 5) / 100);
        }
    }

    /**
     * rotate the wheel by the given angle
     *
     * @param degrees
     */
    private void moveCurve(float degrees) {

        matrix.postTranslate(0, -degrees);
        VerticalRuler.this.setImageMatrix(matrix);

        // add the rotation to the total rotation
        totalDistance -= degrees;
    }

    private void drawCurve(Canvas canvas, float coordinateY, float value) {
//        curvePaint.setColor(Color.parseColor("#000000"));
        if ((totalDistance + coordinateY) >= (wheelHeight - 3 * divDistance)) {
            curvePaint.setColor(Color.parseColor("#33000000"));
        } else {
            curvePaint.setColor(Color.parseColor("#000000"));
        }
        curvePaint.setStrokeWidth(2);
        curvePaint.setTextSize(26);
        DecimalFormat decimalFormat = new DecimalFormat(".00");
        String p = decimalFormat.format(value);
        canvas.drawText(p, (float) (wheelWidth / 2 - 20),
                (float) (totalDistance + (divDistance / 2) + coordinateY),
                curvePaint);

    }

    private void drawLine(Canvas canvas, float coordinateY) {
        if ((totalDistance + coordinateY) >= (wheelHeight - 3 * divDistance)) {
            mPaint.setColor(Color.parseColor("#33000000"));
        } else {
            mPaint.setColor(Color.parseColor("#000000"));
        }
        mPaint.setStrokeWidth(longLineB_H);
        canvas.drawLine((float) (wheelWidth - 20 - longLineB_W),
                (float) (totalDistance + coordinateY),
                (float) (wheelWidth - 20),
                (float) (totalDistance + coordinateY), mPaint);
        mPaint.setStrokeWidth(shortLineS_H);
        for (int i = 1; i <= 4; i++) {
            if ((totalDistance + coordinateY + i * divDistance) >= (wheelHeight - 3 * divDistance)) {
                mPaint.setColor(Color.parseColor("#33000000"));
            } else {
                mPaint.setColor(Color.parseColor("#000000"));
            }
            canvas.drawLine((float) (wheelWidth - 20 - shortLineS_W),
                    (float) (totalDistance + coordinateY + i * divDistance),
                    (float) (wheelWidth - 20), (float) (totalDistance
                            + coordinateY + i * divDistance), mPaint);
        }

    }

    /**
     * Interface to to observe user selection changes.
     */
    public interface ChangeListener {
        /**
         * Called when user selects a new position in the wheel menu.
         *
         * @param selectedPosition the new position selected.
         */
        public void onSelectionChange(float selectedPosition);
    }

    // listener for touch events on the wheel
    private class WheelTouchListener implements OnTouchListener {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Log.d("onTouch", "x:" + event.getX() + ",y:" + event.getY());
            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(event);
            switch (event.getAction()) {

                case MotionEvent.ACTION_DOWN:
                    mScroller.forceFinished(true);
                    startY = event.getY();
                    break;

                case MotionEvent.ACTION_MOVE:
                    float currentY = event.getY();
                    if (((totalDistance - (startY - currentY)) < minDistance
                            - wheelHeight)
                            || ((totalDistance - (startY - currentY)) > maxDistance
                            + wheelHeight)) {
                        reBound();
                    } else {
                        moveCurve((float) (startY - currentY));
                        startY = currentY;
                        selectedPosition = pointCurve
                                + (int) (totalDistance / divDistance);
                        if (wheelChangeListener != null) {
                            wheelChangeListener.onSelectionChange(selectedPosition);
                        }
                    }
                    break;

                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:

                    startY = 0;
                    countVelocityTracker(event);
                    break;
            }

            return true;
        }
    }

    private void countVelocityTracker(MotionEvent event) {
        mVelocityTracker.computeCurrentVelocity(1000, 2500);
        float yVelocity = mVelocityTracker.getYVelocity();
        if (Math.abs(yVelocity) > mMinVelocity) {
//            Log.d("ruler", "countVelocityTracker:mMinVelocity" + mMinVelocity
//                    + ",yVelocity:" + yVelocity);
            mScroller.fling(0, 0, 0, (int) -yVelocity, 0, 0, Integer.MIN_VALUE,
                    Integer.MAX_VALUE);
        } else {
            reBound();

        }
    }

    private void reBound() {
        float over = totalDistance % divDistance;
        Log.d("reBound", "reBound over" + over);
        float distance = 0;
        if (over >= divDistance / 2) {
            distance = over - divDistance;
        } else if (over <= -divDistance / 2) {
            distance = over + divDistance;
        } else {
            distance = over;
        }
        moveCurve(distance);

        if (totalDistance < minDistance + 5 * divDistance) {
            moveCurve((float) (totalDistance - minDistance - 5 * divDistance));
        } else if (totalDistance > maxDistance - 5 * divDistance) {
            moveCurve((float) (totalDistance - maxDistance + 5 * divDistance));
        }

        selectedPosition = pointCurve + (int) (totalDistance / divDistance);
        if (wheelChangeListener != null) {
            wheelChangeListener.onSelectionChange(selectedPosition);
        }
    }
}
